﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.ServiceModel.Description;
using System.Net;
using System.Security.Principal;
using System.ServiceModel.Channels;
using Authorization.Properties;
using System.ComponentModel;
using System.Runtime.Remoting.Messaging;

namespace Authorization
{
    /// <summary>
    /// Classe Singleton para controlar a sincronizacao dos servicos.
    /// Jose Mauro - Rerum 30/05/2011 IM120338
    /// </summary>
    class AuthorizationSynchronizeManager
    {
        #region Attributes

        /// <summary>
        /// Objeto responsavel por controlar as requisicoes de atualizacao
        /// </summary>
        private static AuthorizationSynchronizeManager manager = new AuthorizationSynchronizeManager();
        /// <summary>
        /// Indica que o servico esta realizando um operacao de atualizacao.
        /// </summary>
        private bool IsBusy { get; set; }
        /// <summary>
        /// Delegate para controlar as requisicoes de sincronizacao.
        /// </summary>
        private delegate void UpdateCacheDelegate();

        #endregion

        #region Constructor

        private AuthorizationSynchronizeManager() { }

        #endregion

        #region Methods

        /// <summary>
        /// Recupera a instancia Singleton para processar o sincronismo.
        /// </summary>
        /// <returns></returns>
        public static AuthorizationSynchronizeManager SynchronizeManager()
        {
            return manager;
        }

        /// <summary>
        /// Cria o binding padrao para comunicacao da sincronizacao.
        /// </summary>
        /// <returns></returns>
        private NetTcpBinding GetNetTcpBinding()
        {
            var binding = new NetTcpBinding(SecurityMode.Transport);

            binding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Windows;
            binding.Security.Transport.ProtectionLevel = System.Net.Security.ProtectionLevel.EncryptAndSign;
            binding.Security.Mode = SecurityMode.Transport;
            binding.MaxReceivedMessageSize = 524288;
            binding.ReaderQuotas.MaxStringContentLength = 524288;
            binding.ReaderQuotas.MaxArrayLength = 1638400;
            binding.CloseTimeout.Add(new TimeSpan(00, 30, 00));
            binding.OpenTimeout.Add(new TimeSpan(00, 30, 00));
            binding.ReceiveTimeout.Add(new TimeSpan(00, 30, 00));

            return binding;
        }

        /// <summary>
        /// Atualiza o cache dos servidores NLB levando em conta a lista pre-configurada pela aplicacao.
        /// </summary>
        public void UpdateCache()
        {
            try
            {
                lock (manager)
                {
                    if (!IsBusy)
                    {
                        IsBusy = true;
                        UpdateCacheDelegate updateCache = new UpdateCacheDelegate(UpdateCacheAsync);
                        AsyncCallback completedCallback = new AsyncCallback(UpdateCacheAsyncCompleted);
                        AsyncOperation async = AsyncOperationManager.CreateOperation(null);
                        updateCache.BeginInvoke(completedCallback, async);
                    }
                }
            }
            catch (Exception ex)
            {
                var error = new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.StoreError, ex);
                error.Raise();
                IsBusy = false;
            }
        }

        /// <summary>
        /// Realiza a sincronizacao com os demais servidores.
        /// </summary>
        private void UpdateCacheAsync()
        {
            try
            {
                Binding binding = GetNetTcpBinding(); // Binding utilizado para comunicacao com o server.

                // Credenciais enviadas ao servidor com base no binding configuration do servico
                ClientCredentials clientCredentials = new ClientCredentials();
                clientCredentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Delegation;
                clientCredentials.UserName.UserName = Settings.Default.Username;
                clientCredentials.UserName.Password = Settings.Default.Password;

                // Lista de servidores para atualizacao
                string[] addresses = Settings.Default.AddressForSynchronization.Split(';');
                foreach (string address in addresses)
                {
                    EndpointAddress endPoint = new EndpointAddress(string.Format("net.tcp://{0}/AuthorizationSynchronizeService.svc/v1/nettcp", address));
                    ClientAuthorizationSynchronizeService proxy = new ClientAuthorizationSynchronizeService(binding, endPoint);
                    try
                    {
                        var defaultCredentials = proxy.Endpoint.Behaviors.Find<ClientCredentials>();
                        proxy.Endpoint.Behaviors.Remove(defaultCredentials);
                        proxy.Endpoint.Behaviors.Add(clientCredentials);
                        proxy.ChannelFactory.CreateChannel().UpdateCache();
                    }
                    catch (Exception ex)
                    {
                        var error = new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.StoreError, ex);
                        error.Raise();
                    }
                    finally
                    {
                        if (proxy.State == CommunicationState.Opened)
                        {
                            proxy.Close();
                        }
                    }

                }
            }
            catch (Exception ex)
            {
                var error = new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.StoreError, ex);
                error.Raise();
            }
            finally
            {
                lock (manager)
                {
                    IsBusy = false;
                }
            }
        }

        /// <summary>
        /// Metodo disparado ao terminar a atualizacao da lista de servidores necessarios.
        /// </summary>
        /// <param name="ar">objeto representando a operacao realizada.</param>
        private void UpdateCacheAsyncCompleted(IAsyncResult ar)
        {
            try
            {
                UpdateCacheDelegate updateCache = (UpdateCacheDelegate)((AsyncResult)ar).AsyncDelegate;
                updateCache.EndInvoke(ar);
            }
            catch (Exception ex)
            {
                var error = new WebAuthorizationErrorEvent(ex.Message, this, AuthorizationEventCode.StoreError, ex);
                error.Raise();
            }
            finally
            {
                lock (manager)
                {
                    IsBusy = false;
                }
            }
        }

        #endregion
    }
}
